using System.Collections.Generic;
using System.Windows.Forms;
using System.Diagnostics;
using Core;
using FindItNow.UI;
using System.Text.RegularExpressions;
using System.Linq;
using EnvDTE;

namespace FindItNow.Features
{
	public class FindInSolutionFeature : BindableCommandFeature
	{
		private readonly List<FindRequest> _requests = new List<FindRequest>();
		private List<string> _extensions = new List<string>();

		public FindInSolutionFeature(Ide ide, OutputPane outputPane)
            : base("Find in Solution", "Global::ctrl+shift+g", ide, outputPane)
		{
			Ide.SolutionEvents.Opened += OnSolutionOpened;		
		}

		void OnSolutionOpened()
		{
			_extensions = Ide.FindExtensionsInSolution();
		}

		struct FindTaskContext
		{
			public FindRequest Request;
			public List<string> Paths;
		}

		public override void OnExecute()
		{
			string query = Ide.SelectedText;

			if (query == "")
				query = Ide.WordUnderCarat;

			var form = new FindForm(query, _requests, _extensions);

			if (form.ShowDialog(Ide.MainWindow) != DialogResult.OK)
				return;

			FindRequest request = _requests[_requests.Count - 1];

			var extensions = new List<string>();

			if (request.FileTypes.Trim().Length > 0)
				extensions.AddRange(request.FileTypes.Split(new[] { ';', ',' }));

			List<string> paths;
			
			switch (request.LookIn)
			{
				case FilesSource.Solution:
					paths = Ide.FindFilesInSolution(extensions);
					break;

				case FilesSource.ActiveFile:
					paths = new List<string> {Ide.Dte.ActiveDocument.FullName};
					break;

				case FilesSource.Custom:
					paths = Ide.FindFiles(request.LookInPath, extensions);
					break;

				default:
					paths = new List<string>();
					break;
			}

			System.Threading.ThreadPool.QueueUserWorkItem(ExecuteFindRequest, new FindTaskContext { Request = request, Paths = paths });
		}

		private string DescribeSource(FindRequest request)
		{
			switch (request.LookIn)
			{
				case FilesSource.Solution:
					return "Entire solution";

				case FilesSource.ActiveFile:
					return "Current file";

				case FilesSource.Custom:
					return request.LookInPath;

				default:
					return "";
			}
		}

		private void ExecuteFindRequest(object context)
		{
			List<string> paths = ((FindTaskContext) (context)).Paths;
			FindRequest request = ((FindTaskContext) (context)).Request;

			Ide.ActivateOutputWindow();

			Stopwatch stopwatch = Stopwatch.StartNew();

			OutputPane.Clear();
			OutputPane.JumpToTop();
			OutputPane.PrintMessage("Query: \"" + request.Query + "\"");
			OutputPane.PrintMessage("Options: " + "case=" + (request.MatchCase ? "match" : "ignore") + ", match=" + (request.WholeWord ? "exact" : "partial"));
			OutputPane.PrintMessage("Source: " + DescribeSource(request));
			OutputPane.PrintMessage("Finding... ", false);

			Application.DoEvents();

			string[] tokens = request.Query.Split(' ');

			var regexes = tokens.Select(token => new Regex((request.WholeWord ? "\\b" : "") + Regex.Escape(token) + (request.WholeWord ? "\\b" : ""), request.MatchCase ? 0 : RegexOptions.IgnoreCase)).ToList();
			var results = new List<SearchResult>();

			FindHelpers.BuildSearchResults(results, paths, regexes);

		    OutputPane.PrintMessage("Complete");

			int minScore = request.DisplayAllMatches ? 0 : tokens.Length;

			var sorted = 
				from result in results
				where result.AreaScore >= minScore
				orderby result.LineScore descending, result.AreaScore descending, result.Path, result.Line
				select result;

			SearchResult previous = null;

			int qualityBase = tokens.Length + 1;
			int best = qualityBase * qualityBase - 1;

			long findTime = stopwatch.ElapsedMilliseconds;

			foreach (var result in sorted)
			{
				if (previous == null || (previous.LineScore != result.LineScore) || (previous.AreaScore != result.AreaScore))
				{
					int quality = qualityBase * result.LineScore + result.AreaScore;

					string title = "\nMatch Quality: " + (int)(100.0f * quality / best) + "%";

					OutputPane.PrintMessage(title);
					OutputPane.PrintMessage(new string('-', title.Length));
				}

				OutputPane.PrintMessage(result.Path + "(" + result.Line + "): " + result.Text);

				previous = result;
			}

			long printTime = stopwatch.ElapsedMilliseconds;

			Ide.ActivateOutputWindow();

			OutputPane.PrintMessage("");
			OutputPane.PrintMessage("Total files searched: " + paths.Count);
			OutputPane.PrintMessage("Matching lines: " + results.Count);
			OutputPane.PrintMessage("Find Time: " + findTime + " ms");
			OutputPane.PrintMessage("Output Time: " + (printTime - findTime) + " ms");

			Application.DoEvents();

			OutputPane.JumpToTop();
		}
	}
}